package controller;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

import model.AIDataNode;

/**
 * This class has to validate the implemented Algorithms. It splits up into trainingsset and testset if neccessary
 * @author walonka
 * @version 1.0
 */
public class Validation {
	/**
	 * This class splits up an ArrayList into a specified part of a list
	 * @param inputlist source of the new list
	 * @param start includes startpoint
	 * @param end not included endpoint
	 * @return returns a new ArrayList
	 */

	/**
	 * Counts for the Amount of Class0 and Class1
	 * @param inputdata Arraylist of data to be counted
	 * @return [0] #class0 [1] #class1 
	 */
	public int[] getClassAmount(ArrayList<AIDataNode> inputdata){
		int class0=0;
		int class1=0;
		for(int i=0; i<inputdata.size();i++){
			if (inputdata.get(i).getKlass()==0){
				class0++;
			}
			else{
				class1++;
			}
		}
		
		int[] returndata={class0,class1};
		return returndata;
	}
	
	/**
	 * This function returns a double Array of ArrayLists [][]. It's 10 arrays including a trainingset at [i][0] and a testset at [i][1].
	 * @param inputdata Array that will be splittet up
	 * @return first is training, second is test
	 */
	public ArrayList<AIDataNode>[][] stratified10Split(ArrayList<AIDataNode> inputdata){
		//data precalculation
		int range = (int) (inputdata.size()/10.0+0.5); //list of each Testset
		int[] classAmounts = getClassAmount(inputdata);
		double class0Percentage = (double)classAmounts[0]/(classAmounts[0]+classAmounts[1]);
		double class1Percentage = (double)classAmounts[1]/(classAmounts[0]+classAmounts[1]);
		int class0Amount = (int) (range*class0Percentage+0.5);
		int class1Amount = (int) (range*class1Percentage+0.5);
		
		@SuppressWarnings("unchecked")
		ArrayList<AIDataNode>[][] returndata = new ArrayList[10][2];
		
		
		for(int i=0;i<10;i++){
			//creating deep workingcopy & preparing data
			ArrayList<AIDataNode> workingdata = new ArrayList<AIDataNode>();
			for (int j=0; j<inputdata.size();j++){
				workingdata.add(inputdata.get(j));
			}
			int class0Counter=0;
			int class1Counter=0;
			ArrayList<AIDataNode> testset = new ArrayList<AIDataNode>();
			
			
			//starting to add data
			int currentPosition=range*i;
			while(class0Counter<class0Amount | class1Counter<class1Amount){
				if(currentPosition==workingdata.size()){
					currentPosition=0;
					break; //We have to do it without overlapping, even if that might be stupid :-(
				}
				if(class0Counter<class0Amount&&workingdata.get(currentPosition).getKlass()==0){
					testset.add(workingdata.get(currentPosition));
					workingdata.remove(currentPosition);
					class0Counter++;
				}
				else if(class1Counter<class1Amount&&workingdata.get(currentPosition).getKlass()==1){
					testset.add(workingdata.get(currentPosition));
					workingdata.remove(currentPosition);
					class1Counter++;
				}
				else{
					currentPosition++;
				}
			}
			ArrayList<AIDataNode> trainingset =  workingdata;
            returndata[i][1]=testset; 
            returndata[i][0]=trainingset;
		}
		return returndata;
	}
	
	/**
	 * Calculates the accuracy of an input
	 * @param correct
	 * @param wrong
	 * @return returns the accuracy as a String including the %-char
	 */
	public String accuracy(int correct, int wrong){
		double tmp = (double)correct/((double)correct+wrong);
		tmp=tmp*100;
		String ret=""+tmp;
		int min = Math.min(4, ret.length());
		ret=ret.substring(0, min);
		ret+="%";
		return ret;
	}
	
	/**
	 * Method to test the Bayes
	 * @param node
	 */
	public void testBayes(ArrayList<AIDataNode> node){
		System.out.println("Testing NaiveBayes");
		ArrayList<AIDataNode>[][] validationdata=stratified10Split(node);
		int totalCorrect=0;
		int totalFalse=0;
		for (int i=0; i<validationdata.length;i++){
			//System.out.println("Run number "+i);
			NaiveBayes bayes = new NaiveBayes(validationdata[i][0], validationdata[i][1]);
			bayes.train();
			int tmp[] = bayes.test();
			totalCorrect+=tmp[0];
			totalFalse+=tmp[1];
			System.out.println("+++Correct in run "+i+":"+tmp[0]+"---Wrong in run "+i+":"+tmp[1]+" Accuracy is:"+accuracy(tmp[0], tmp[1]));
		}
		System.out.println("+++Correct: "+totalCorrect+"---Wrong:"+totalFalse+" Accuracy is:"+accuracy(totalCorrect, totalFalse));

	}
	/**
	 * Method to test the KNN
	 * @param node
	 */
	public void testKNN(ArrayList<AIDataNode> node, int k){
		System.out.println("Testing "+k+"NN");
		ArrayList<AIDataNode>[][] validationdata=stratified10Split(node);
		int totalCorrect=0;
		int totalFalse=0;
		for (int i=0; i<validationdata.length;i++){
			//System.out.println("Run number "+i);
			kNearestNEighbor knn = new kNearestNEighbor(validationdata[i][0], validationdata[i][1],k);
			int tmp[] = knn.test();
			System.out.println("+++Correct in run "+i+":"+tmp[0]+"---Wrong in run "+i+":"+tmp[1]+" Accuracy is:"+accuracy(tmp[0], tmp[1]));
			totalCorrect+=tmp[0];
			totalFalse+=tmp[1];
		}
		System.out.println("+++Correct: "+totalCorrect+"---Wrong:"+totalFalse+" Accuracy is:"+accuracy(totalCorrect, totalFalse));
	}
	
	public void printFolds(ArrayList<AIDataNode> node){
		ArrayList<AIDataNode>[][] validationdata=stratified10Split(node);
		try {
			FileWriter fw = new FileWriter("pima-folds.csv");
			@SuppressWarnings("resource")
			BufferedWriter out = new BufferedWriter(fw);
			for (int i=0; i<validationdata.length;i++){
				out.write("fold"+(i+1)+"\n");
				for(int j=0; j<validationdata[i].length;j++){
					for(int k=0; k<validationdata[i][j].size();k++){
						out.write(validationdata[i][j].get(k).toString()+"\n");
					}
					out.write("\n");			
				}
			}
			out.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}
